/************************************************************
 * arch/8051/src/up_head.S
 *
 *   Copyright (C) 2007, 2014 Gregory Nutt. All rights reserved.
 *   Author: Gregory Nutt <gnutt@nuttx.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 3. Neither the name Gregory Nutt nor the names of its contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 ************************************************************/

/************************************************************
 * Included Files
 ************************************************************/

#include <nuttx/config.h>
#include <nuttx/irq.h>
#include "up_internal.h"

	.module	up_head
	.optsdcc -mmcs51 --model-large

/************************************************************
 * Private Data
 ************************************************************/

	.area REG_BANK_0	(REL,OVR,DATA)
	.ds 8

#ifndef CONFIG_ARCH_8051_NOSYSTIMER
	.area	XSEG
_g_timer0tick:
	.ds 1
#endif

/************************************************************
 * Public Data
 ************************************************************/

	.globl	_g_irqtos
	.globl	_g_irqcontext
	.globl	_g_irqregs

/************************************************************
 * Public Functions
 ************************************************************/

	.globl	_irq_dispatch
	.globl	_up_restoreregisters

/************************************************************
 * Program entry points
 ************************************************************/

/* Program entry is through PROGRAM_BASE.  This is just a
 * branch to our start up logic.
 */

	.area	CODE1 (ABS)
	.org	PROGRAM_BASE
	ljmp	start

/* These are indirect interrupt vectors.  Logic in PAULMON2,
 * captures the interrupt vectors (near address 0x0000) and
 * re-routes them through the following entry points.
 *
 * Each of these saves acc and ie then passes the IRQ number
 * to higher level logic in a
 */

	.org	PM2_VECTOR_EXTINT0
	push	acc
	mov	a, #EXT_INT0_IRQ
	ljmp	_up_interrupt

	.org	PM2_VECTOR_TIMER0
	push	acc
#ifdef CONFIG_ARCH_8051_NOSYSTIMER
	mov	a, #TIMER0_IRQ
	ljmp	_up_interrupt
#else
	ljmp	_up_timer0
#endif
	.org	PM2_VECTOR_EXTINT1
	push	acc
	mov	a, #EXT_INT1_IRQ
	ljmp	_up_interrupt

	.org	PM2_VECTOR_TIMER1
	push	acc
	mov	a, #TIMER1_IRQ
	ljmp	_up_interrupt

	.org	PM2_VECTOR_UART
	push	acc
	mov	a, #UART_IRQ
	ljmp	_up_interrupt

	.org	PM2_VECTOR_TIMER2
	push	acc
	mov	a, #TIMER2_IRQ
	ljmp	_up_interrupt

/************************************************************
 * Name: start
 *
 * Description:
 *   This is the initial entry point into NuttX
 *
 ************************************************************/

start:
	mov	sp, #(STACK_BASE-1)

#ifdef CONFIG_ARCH_LEDS
	lcall	_board_led_initialize
#endif

	ljmp	_os_start

/************************************************************
 * Name: up_timer0
 *
 * Description:
 *   Timer 0, mode 0 can be used as a system timer.  In that
 *   mode, the 1.8432 MHz clock is divided by 32.  A single
 *   8-bit value is incremented at 57600 Hz, which results
 *   in 225 Timer 0 overflow interrupts per second.
 *
 *   The Timer0 interrupt vectors to this point which then
 *   does a software divide by 2 to get a system timer of
 *   112.5Hz.
 *
 *   On Entry:
 *
 *   (1) acc on the stack and
 *   (2) the IRQ number(TIMER0_IRQ) in the accumulator
 *
 ************************************************************/

#ifndef CONFIG_ARCH_8051_NOSYSTIMER
_up_timer0:
	ar2 = 0x02
	ar3 = 0x03
	ar4 = 0x04
	ar5 = 0x05
	ar6 = 0x06
	ar7 = 0x07
	ar0 = 0x00
	ar1 = 0x01

	/* ACC already on the stack; push IE.  Then disable interrupts */

	push	ie
	clr	ea

	/* Save the remaining registers with interrupts disabled
	 *
	 * a, ie, and dptr go on the stack.
	 */

	push	dpl
	push	dph

	/* Increment the tick counter */

	mov	dptr, #_g_timer0tick
        movx    a, @dptr
	inc	a
	movx	@dptr, a

	/* If bit 0 is '0', then just return from the interrupt */

	anl	a, #0x01
	jnz	00101$
	ljmp	_up_timer0exit

	/* If bit 0 is '1', then process the interrupt */

00101$:
	mov	a, #TIMER0_IRQ
	sjmp	_up_timer0join
#endif

/************************************************************
 * Name: up_interrupt
 *
 * Description:
 *   All interrupts vector to this point with:
 *
 *   (1) acc on the stack and
 *   (2) the IRQ number in the accumulator
 *
 ************************************************************/

_up_interrupt:
	ar2 = 0x02
	ar3 = 0x03
	ar4 = 0x04
	ar5 = 0x05
	ar6 = 0x06
	ar7 = 0x07
	ar0 = 0x00
	ar1 = 0x01

	/* ACC already on the stack; push IE.  Then disable interrupts */

	push	ie
	clr	ea

	/* Save the remaining registers with interrupts disabled
	 *
	 * a, ie, and dptr go on the stack.
	 */

	push	dpl
	push	dph

_up_timer0join:
	/* Other registers go into the IRQ register save area */

	push	acc
	mov	dptr, #_g_irqregs
	lcall	_up_saveregisters

	/* Show interrupt status on the LEDs */

#ifdef CONFIG_ARCH_LEDS
	mov	dpl, #LED_INIRQ
	lcall	_board_led_on
#endif

	/* Save the IRQ number in r2 */

	pop	ar2

	/* Mark that we are in an interrupt and provide the top
	 * of stack pointer to the context switching logic.
	 */

	mov	dptr, #_g_irqtos
	mov	a, sp
        movx    @dptr, a

	/* Nullify the context pointer.  If a context switch is
	 * needed, this will be set to the address of the context
	 * structure.
	 */

	mov	dptr, #_g_irqcontext
	clr	a
	movx	@dptr,a
	inc	dptr
	movx	@dptr,a

	/* Now call void irq_dispatch(int irq, FAR void *context)
         *
	 * First, create the first argument as (int)irqno
	 */

        mov     dpl, r2
	mov	dph, #0

        /* Create the second argument (void *context) on the stack */

	push	sp
	clr	a
	push	acc

	/* Then dispatch the IRQ. */

	lcall	_irq_dispatch
	pop	acc
	pop	acc

	/* Indicate that we are no longer in an interrupt */

	mov	dptr, #_g_irqtos
	clr	a
        movx    @dptr, a

	/* Check if a context switch is pending */

	mov	dptr,#_g_irqcontext
	movx	a, @dptr
	mov     r2, a
	inc	dptr
	movx	a, @dptr
	mov	r3, a

	orl	a, r2
	jnz	00001$

	/* No context switch is pending.  Restore registers
	 * from the interrupt register save area.
	 */

	mov	dptr, #_g_irqregs
	sjmp	00004$

00001$: /****************************************************/

	/* A context switch is pending, clear g_irqcontext */

	mov	dpl, r2
	mov	dph, r3
	clr	a
	movx	@dptr, a
	inc	dptr
	movx	@dptr, a

#ifdef CONFIG_INTERRUPT_FRAME_DUMP
	mov	dpl, r2
	mov	dph, r3
	push	ar2
	push	ar3
	lcall	_up_dumpframe
	pop	ar3
	pop	ar2
#endif

	/* Register usage in the following:
	 *
	 * R0   - Holds working the 8-bit IRAM pointer
	 * R1   - Not used
	 * R2-3 - Holds the working 16-bit XRAM pointer
	 * R4   - Holds the working byte count
	 * R5   - Holds the new stack pointer
	 * R6-7 - Not used
	 */

	/* Fetch r4 = context->nbytes */

	mov	dpl, r2
	mov	dph, r3
	movx	a, @dptr
	mov	r4, a

	/* Save the new stack pointer in r5 */

	add	a, #(STACK_BASE-1)
	mov	r5, a

	/* Save r2-3 = &context->stack */

	inc	dptr
	push	dpl
	push	dph
	mov	r2, dpl
	mov	r3, dph

	/* Set r0 = stack base address */

	mov	r0, #STACK_BASE

	/* Top of the copy loop */
00002$:
	mov	a, r4	/* a = bytes left to transfer */
	dec	r4	/* (for next time through the loop) */
	jz	00003$	/* Jump if a = 0 (done) */

	/* Fetch the next byte from context->stack */

	mov	dpl, r2
	mov	dph, r3
	movx	a,@dptr

	/* Increment the XRAM pointer */

	inc	dptr
	mov	r2, dpl
	mov	r3, dph

	/* Save the next byte into IRAM */

	mov	@r0, a

	/* Increment the IRAM pointer */

	inc	r0
	sjmp	00002$

	/* The entire stack has been copied from XRAM into
	 * IRAM.  Set the new stack pointer
	 */

00003$:
	pop	dph
	pop	dpl
	mov	sp, r5

#ifdef CONFIG_INTERRUPT_FRAME_DUMP
	push	dpl
	push	dph
	lcall	_up_dumpstack
	pop	dph
	pop	dpl
#endif
	/* Get the pointer to the register save area */

	mov	a, #STACK_SIZE
	add	a, dpl
	mov	dpl, a
	clr	a
	addc	a, dph
	mov	dph, a
	
00004$: /****************************************************/

	/* Restore the context from the register save area
	 * and return from the interrupt.  At this point, dptr
	 * holds the pointer to the memory region that holds
	 * the register save area. This could be either
	 * g_irqregs (no context switch) or &g_irqcontext->regs
	 * (context switch).
	 */

#ifdef CONFIG_ARCH_LEDS
	push	dpl
	push	dph
	mov	dpl, #LED_INIRQ
	lcall	_board_led_off
	pop	dph
	pop	dpl
#endif
	/* Restore registers from the register save area */

	lcall	_up_restoreregisters

_up_timer0exit:
	/* Restore registers from the stack and return */

	pop	dph
	pop	dpl

	/* Restore the interrupt state per the stored IE value */

	pop	acc
	jb	acc.7,00005$
	clr	ie.7
	sjmp	00006$
00005$:
	setb	ie.7

00006$:
	pop	acc
	reti
